]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
calendar: Add NotificationSection to message list
authorFlorian Müllner <fmuellner@gnome.org>
Wed, 11 Feb 2015 19:41:56 +0000 (20:41 +0100)
committerFlorian Müllner <fmuellner@gnome.org>
Fri, 20 Feb 2015 16:39:25 +0000 (17:39 +0100)
Display notifications that have not been dismissed in the message
list - eventually this will replace the existing message tray summary.
Notification messages show icon, title and one line of the body and
can be clicked to activate the default action. However they cannot be
expanded, so other actions or the full body text are not accessible
in this mode.

https://bugzilla.gnome.org/show_bug.cgi?id=744817

data/gnome-shell-theme.gresource.xml
data/theme/no-notifications.svg [new file with mode: 0644]
js/ui/calendar.js
js/ui/main.js

index 9e270d05683ccb77c7a61f390e645b67d1ac0bdc..f1453253163243ff1f838dbd35c5016b96beaaaf 100644 (file)
@@ -20,6 +20,7 @@
     <file>logged-in-indicator.svg</file>
     <file>more-results.svg</file>
     <file>no-events.svg</file>
+    <file>no-notifications.svg</file>
     <file>noise-texture.png</file>
     <file>page-indicator-active.svg</file>
     <file>page-indicator-inactive.svg</file>
diff --git a/data/theme/no-notifications.svg b/data/theme/no-notifications.svg
new file mode 100644 (file)
index 0000000..23af556
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64px"
+   height="64px"
+   id="svg3393"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="New document 2">
+  <defs
+     id="defs3395" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="5.5"
+     inkscape:cx="32"
+     inkscape:cy="32"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     inkscape:window-width="697"
+     inkscape:window-height="613"
+     inkscape:window-x="100"
+     inkscape:window-y="77"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata3398">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       style="display:inline"
+       transform="matrix(4,0,0,4,0.29733827,-0.35415646)"
+       id="g19245">
+      <g
+         id="g19247"
+         inkscape:label="status"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19249"
+         inkscape:label="devices"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19251"
+         inkscape:label="apps"
+         transform="translate(-323.02908,-649.02581)">
+        <path
+           inkscape:connector-curvature="0"
+           d="m 331.9377,653 c 0.0187,0.16677 0.0625,0.32822 0.0625,0.5 0,2.48528 -2.01472,4.5 -4.5,4.5 -0.11769,0 -0.22834,-0.0224 -0.34375,-0.0312 l 0,2.21875 c 0,1.00412 0.80838,1.8125 1.8125,1.8125 l 1.54511,-5e-5 2,2.04688 2.0625,-2.04688 1.61114,0 c 1.00413,0 1.8125,-0.80838 1.8125,-1.8125 l 0,-5.375 c 0,-1.00412 -0.80837,-1.8125 -1.8125,-1.8125 z"
+           id="path19253"
+           sodipodi:nodetypes="csscsscccssssc"
+           style="opacity:0.5;color:#000000;fill:#c3c3c3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+        <path
+           inkscape:connector-curvature="0"
+           d="m 327.5002,650 c -1.933,0 -3.5,1.567 -3.5,3.5 0,1.933 1.567,3.5 3.5,3.5 1.933,0 3.5,-1.567 3.5,-3.5 0,-1.933 -1.567,-3.5 -3.5,-3.5 z m -0.53125,1 1.03125,0 -0.0625,1.375 a 0.19951718,0.19951718 0 0 0 0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.125,0.125 0.19951718,0.19951718 0 0 0 0.0312,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 l 1.15625,-0.75 0.5,0.90625 -1.21875,0.625 a 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0.0937 0.19951718,0.19951718 0 0 0 0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0625 0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0312 l 1.25,0.625 -0.53125,0.90625 -1.15625,-0.781 a 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,0 0.19951718,0.19951718 0 0 0 -0.125,0.0937 0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0,0.0625 l 0.0625,1.3751 -1.03125,0 0.0937,-1.375 a 0.19951718,0.19951718 0 0 0 -0.0312,-0.0937 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 -0.0937,0.0312 l -1.1875,0.78125 -0.5,-0.90625 1.25,-0.625 a 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 0 0 0 -0.0312,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 l -1.25,-0.625 0.5,-0.90625 1.1875,0.75 a 0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0312,0 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 L 326.96895,651 z"
+           id="path19255"
+           style="color:#000000;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      </g>
+      <g
+         id="g19257"
+         inkscape:label="places"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19259"
+         inkscape:label="mimetypes"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19261"
+         inkscape:label="emblems"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19263"
+         inkscape:label="emotes"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19265"
+         inkscape:label="categories"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19267"
+         inkscape:label="actions"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+    </g>
+  </g>
+</svg>
index 8204315d719b97ba0cfddc70937f817b56562be3..ecb609bf09cca2963c8d384f9ee7aebf0c181053 100644 (file)
@@ -15,6 +15,7 @@ const Meta = imports.gi.Meta;
 const Shell = imports.gi.Shell;
 
 const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
 const Tweener = imports.ui.tweener;
 const Util = imports.misc.util;
 
@@ -974,6 +975,61 @@ const Message = new Lang.Class({
 });
 Signals.addSignalMethods(Message.prototype);
 
+const NotificationMessage = new Lang.Class({
+    Name: 'NotificationMessage',
+    Extends: Message,
+
+    _init: function(notification) {
+        this.notification = notification;
+
+        this.setUseBodyMarkup(notification.bannerBodyMarkup);
+        this.parent(notification.title, notification.bannerBodyText);
+
+        this.setIcon(this._getIcon());
+
+        this.connect('close', Lang.bind(this,
+            function() {
+                this._closed = true;
+                this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED);
+            }));
+        notification.connect('destroy', Lang.bind(this,
+            function() {
+                if (!this._closed)
+                    this.emit('close');
+            }));
+        this._updatedId = notification.connect('updated',
+                                               Lang.bind(this, this._onUpdated));
+    },
+
+    _getIcon: function() {
+        if (this.notification.gicon)
+            return new St.Icon({ gicon: this.notification.gicon, icon_size: 48 });
+        else
+            return this.notification.source.createIcon(48);
+    },
+
+    _onUpdated: function(n, clear) {
+        this.setIcon(this._getIcon());
+        this.setTitle(n.title);
+        this.setBody(n.bannerBodyText);
+        this.setUseBodyMarkup(n.bannerBodyMarkup);
+    },
+
+    canClear: function() {
+        return !this.notification.resident;
+    },
+
+    _onClicked: function() {
+        this.notification.activate();
+    },
+
+    _onDestroy: function() {
+        if (this._updatedId)
+            this.notification.disconnect(this._updatedId);
+        this._updatedId = 0;
+    }
+});
+
 const MessageListSection = new Lang.Class({
     Name: 'MessageListSection',
 
@@ -1275,6 +1331,109 @@ const EventsSection = new Lang.Class({
     }
 });
 
+const NotificationSection = new Lang.Class({
+    Name: 'NotificationSection',
+    Extends: MessageListSection,
+
+    _init: function() {
+        this.parent('Notifications');
+
+        this._sources = new Map();
+        this._nUrgent = 0;
+
+        Main.messageTray.connect('source-added', Lang.bind(this, this._sourceAdded));
+        Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
+            this._sourceAdded(Main.messageTray, source);
+        }));
+
+        this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
+
+        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
+        this._sessionUpdated();
+    },
+
+    _sourceAdded: function(tray, source) {
+        let obj = {
+            destroyId: 0,
+            notificationAddedId: 0,
+        };
+
+        obj.destroyId = source.connect('destroy', Lang.bind(this, function(source) {
+            this._onSourceDestroy(source, obj);
+        }));
+        obj.notificationAddedId = source.connect('notification-added',
+                                                 Lang.bind(this, this._onNotificationAdded));
+
+        this._sources.set(source, obj);
+    },
+
+    _onNotificationAdded: function(source, notification) {
+        let message = new NotificationMessage(notification);
+
+        let time = new Date().toLocaleFormat(C_("event list time", "%H\u2236%M"));
+        message.setSecondaryActor(new St.Label({ style_class: 'event-time',
+                                                 x_align: Clutter.ActorAlign.END,
+                                                 text: time }));
+
+        let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
+        if (isUrgent) {
+            // Keep track of urgent notifications to keep them on top
+            this._nUrgent++;
+
+            let id = notification.connect('destroy', Lang.bind(this,
+                function() {
+                    notification.disconnect(id);
+                    this._nUrgent--;
+                }));
+        } else if (this.mapped) {
+            // Only acknowledge non-urgent notifications in case it
+            // has important actions that are inaccessible when not
+            // shown as banner
+            notification.acknowledged = true;
+        }
+
+        let index = isUrgent ? 0 : this._nUrgent;
+        this.addMessageAtIndex(message, index, this.actor.mapped);
+    },
+
+    _onSourceDestroy: function(source, obj) {
+        source.disconnect(obj.destroyId);
+        source.disconnect(obj.notificationAddedId);
+
+        this._sources.delete(source);
+    },
+
+    _onMapped: function() {
+        if (!this.actor.mapped)
+            return;
+
+        for (let message of this._messages.keys())
+            if (message.notification.urgency != MessageTray.Urgency.CRITICAL)
+                message.notification.acknowledged = true;
+    },
+
+    _onTitleClicked: function() {
+        this.parent();
+
+        let app = Shell.AppSystem.get_default().lookup_app('gnome-notifications-panel.desktop');
+
+        if (!app) {
+            log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
+            return;
+        }
+
+        app.activate();
+    },
+
+    _syncVisible: function() {
+        this.actor.visible = !this.empty && this._isToday();
+    },
+
+    _sessionUpdated: function() {
+        this._title.reactive = Main.sessionMode.allowSettings;
+    }
+});
+
 const Placeholder = new Lang.Class({
     Name: 'Placeholder',
 
@@ -1284,14 +1443,41 @@ const Placeholder = new Lang.Class({
 
         this._date = new Date();
 
-        let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg');
-        let gicon = new Gio.FileIcon({ file: file });
+        let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg');
+        let otherFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg');
+        this._todayIcon = new Gio.FileIcon({ file: todayFile });
+        this._otherIcon = new Gio.FileIcon({ file: otherFile });
 
-        this._icon = new St.Icon({ gicon: gicon });
+        this._icon = new St.Icon();
         this.actor.add_actor(this._icon);
 
-        this._label = new St.Label({ text: _("No Events") });
+        this._label = new St.Label();
         this.actor.add_actor(this._label);
+
+        this._sync();
+    },
+
+    setDate: function(date) {
+        if (_sameDay(this._date, date))
+            return;
+        this._date = date;
+        this._sync();
+    },
+
+    _sync: function() {
+        let isToday = _sameDay(this._date, new Date());
+        if (isToday && this._icon.gicon == this._todayIcon)
+            return;
+        if (!isToday && this._icon.gicon == this._otherIcon)
+            return;
+
+        if (isToday) {
+            this._icon.gicon = this._todayIcon;
+            this._label.text = _("No Notifications");
+        } else {
+            this._icon.gicon = this._otherIcon;
+            this._label.text = _("No Events");
+        }
     }
 });
 
@@ -1320,6 +1506,9 @@ const MessageList = new Lang.Class({
         this._scrollView.add_actor(this._sectionList);
         this._sections = new Map();
 
+        this._notificationSection = new NotificationSection();
+        this._addSection(this._notificationSection);
+
         this._eventsSection = new EventsSection();
         this._addSection(this._eventsSection);
     },
@@ -1377,5 +1566,6 @@ const MessageList = new Lang.Class({
     setDate: function(date) {
         for (let section of this._sections.keys())
             section.setDate(date);
+        this._placeholder.setDate(date);
     }
 });
index 2fb674c87336f48060cc8a80dd61a862cf161483..eb65458641cefdaf8ec019154336db398768ff7a 100644 (file)
@@ -159,8 +159,8 @@ function _initializeUI() {
     if (LoginManager.canLock())
         screenShield = new ScreenShield.ScreenShield();
 
-    panel = new Panel.Panel();
     messageTray = new MessageTray.MessageTray();
+    panel = new Panel.Panel();
     keyboard = new Keyboard.Keyboard();
     notificationDaemon = new NotificationDaemon.NotificationDaemon();
     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();